package com.augmented.jellybean;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONArray;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Typeface;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.augmented.jellybean.util.ImageParser;
import com.augmented.jellybean.util.JSONParser;
import com.augmented.jellybean.util.information;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class AugmentedJellybeanMapScreenActivity extends FragmentActivity implements LocationListener,
OnMapClickListener, 
OnMapLongClickListener {

	// Initializing variables

	HashMap<String, information> markerMap;
	ArrayList<String> keys;
	GoogleMap mMap;
	View myView;

	LatLng onClick;
	LatLng destination;
	Location myLocation;
	String contextMenuType;	//Set during callback methods, determines which context menu appears
	Marker selectedMarker;

	private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place";
	private static final String TYPE_SEARCH = "/search";
	private static final String OUT_JSON = "/json";

	// This is a Browser key, not to be confused with an Android key
	private static final String API_KEY = "AIzaSyBiDc85xw07AjJPGndg_rmvC8x5Lmczk2c";


	@SuppressLint({ "NewApi", "DefaultLocale" })
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.augmented_jellybean_map_screen);
		myView = findViewById(android.R.id.content);

		// Setting up button listener

		Button btnList = (Button) findViewById(R.id.listview);

		btnList.setOnClickListener(new View.OnClickListener() {

			public void onClick(View arg0) {
				//Starting a new Intent
				Intent nextScreen = new Intent(getApplicationContext(), AugmentedJellybeanListScreenActivity.class);

				//Sending data to another Activity
				nextScreen.putExtra("list_view", markerMap);
				nextScreen.putExtra("keys", keys);
				Log.i("Loading List View", "SUCCESS");

				startActivity(nextScreen);
			}
		});

		// Getting Google Play availability status
		int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());
		FragmentManager myFragmentManager = getSupportFragmentManager();
		SupportMapFragment myMapFragment = (SupportMapFragment) myFragmentManager.findFragmentById(R.id.map);
		mMap = myMapFragment.getMap();

		// Make sure markerMap has no saved data in it, so create a new instance of markerMap
		markerMap = new HashMap<String, information>();
		keys = new ArrayList<String>();

		// The map is a satellite view
		mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

		// Showing status
		if(status!=ConnectionResult.SUCCESS){ // Google Play Services are not available
			int requestCode = 10;
			Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);
			dialog.show();
		} else {
			// Getting reference to the SupportMapFragment of activity_main.xml
			SupportMapFragment fm = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
			mMap = fm.getMap();
			mMap.setMyLocationEnabled(true);

			// Getting LocationManager object from System Service LOCATION_SERVICE
			LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

			// Creating a criteria object to retrieve provider
			Criteria criteria = new Criteria();
			//String provider = locationManager.getBestProvider(criteria, false);
			String provider = locationManager.GPS_PROVIDER;
			Location location = locationManager.getLastKnownLocation(provider);
			myLocation = location;

			
			// Move the camera to the user's location and zoom in!

			if (myLocation != null) {
				onLocationChanged(location);
			} else {
				if(myLocation == null) Log.e("location", "Location is null");
				locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
				locationManager.requestLocationUpdates(provider, 20000, 10, this);	
			}


		}

		// Register the context menu. Find the view first
		registerForContextMenu(myView);
		mMap.setOnMapLongClickListener(this);
		// mMap.setOnMarkerClickListener(this);
		mMap.setInfoWindowAdapter(new InfoWindowAdapter() {
			@Override
			public View getInfoWindow(Marker marker) {
				// Use the default info window
				return null;
			}

			@Override
			public View getInfoContents(Marker marker) {
				View v = getLayoutInflater().inflate(R.layout.augmented_jellybean_content_window, null);
				information currentPlace = markerMap.get(marker.getSnippet());

				String placeTitle = currentPlace.getName();
				String placeAddress = currentPlace.getAddress();

				TextView placeName = ((TextView) v.findViewById(R.id.place_name));
				TextView placeType = ((TextView) v.findViewById(R.id.place_address));
				TextView placeDescription = ((TextView) v.findViewById(R.id.place_description));
				ImageView placeIcon = ((ImageView) v.findViewById(R.id.place_icon));

				// Grab image and reference it with global HashMap of markers
				if (currentPlace.getIcon().equals(null)) {
					// Set icon to a default pictureless image
				} else {
					placeIcon.setImageBitmap(currentPlace.getIcon());
				}

				if (placeTitle != null) {
					// Spannable string allows us to edit the formatting of the text
					SpannableString placeText = new SpannableString(placeTitle);
					placeText.setSpan(new ForegroundColorSpan(Color.RED), 0, placeText.length(), 0);

					placeName.setText(placeText);
				} else {
					placeName.setText("");
				}

				if (placeAddress != null) {
					// Spannable string allows us to edit the formatting of the text
					SpannableString placeAddressText = new SpannableString(placeAddress);
					placeAddressText.setSpan(new ForegroundColorSpan(Color.DKGRAY), 0, placeAddressText.length(), 0);
					placeType.setText(placeAddressText);
				} else {
					placeType.setText("");
				}

				String description = "";

				for (int i = 0; i < currentPlace.getTypeList().size(); i++) {
					description = description.concat(currentPlace.getTypeList().get(i) + ", ");
				}

				description = description.substring(0, description.length()-2);
				SpannableString placeDescriptionText = new SpannableString(description);
				placeDescriptionText.setSpan(new StyleSpan(Typeface.ITALIC), 0, placeDescriptionText.length(), 0);
				placeDescription.setText(placeDescriptionText);

				return v;
			}
		});

		mMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
			@Override
			public void onInfoWindowClick(Marker marker) {
				contextMenuType = "infowindow";
				destination = marker.getPosition();
				openContextMenu(myView);
			}
		});

		// If the Search event was called, we will populate the map with results
		populateMap();
	}

	@Override
	public void onLocationChanged(Location location) {

		// Getting latitude of the current location
		double latitude = location.getLatitude();

		// Getting longitude of the current location
		double longitude = location.getLongitude();

		// Creating a LatLng object for the current location
		LatLng latLng = new LatLng(latitude, longitude);

		// Zoom in the Google Map
		mMap.animateCamera(CameraUpdateFactory.zoomTo(16));

		// Showing the current location in Google Map
		mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));

	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		super.onCreateContextMenu(menu, v, menuInfo);
		menu.setHeaderTitle("");
		MenuInflater inflater = getMenuInflater();

		if(contextMenuType == "map") inflater.inflate(R.menu.map_menu, menu);
		else if(contextMenuType == "infowindow")
			inflater.inflate(R.menu.infowindow_menu, menu);
	}

	// Perform actions when item on context menu is pressed
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.add_marker:
			// Add marker on pressed location
			mMap.addMarker(new MarkerOptions().position(onClick).title("Pressed here"));
			return true;
		case R.id.delete_marker:
			if(selectedMarker != null) {
				selectedMarker.remove();
				selectedMarker = null;
			}
			return true;
		case R.id.getdirections:
			// Opens up Google Maps to show directions
			StringBuilder directions_uri = new StringBuilder("http://maps.google.com/maps?saddr=");
			directions_uri.append(myLocation.getLatitude() + "," + myLocation.getLongitude() + "&daddr=");	
			directions_uri.append(destination.latitude + ",");
			directions_uri.append(destination.longitude);

			//Starts Google Maps app with given locations
			Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(directions_uri.toString()));
			startActivity(intent);

			return true;
		case R.id.streetview:

			DisplayMetrics dm = new DisplayMetrics();
			getWindowManager().getDefaultDisplay().getMetrics(dm);
			int screenWidth = dm.widthPixels * 2;
			int screenHeight = dm.heightPixels * 2;
			String size = Integer.toString(screenWidth) + "x" + Integer.toString(screenHeight);

			// Opens up Google Street View to show destination
			StringBuilder streetview_uri = new StringBuilder("http://maps.googleapis.com/maps/api/streetview?");
			streetview_uri.append("size=" + size);
			streetview_uri.append("&sensor=false&location=");
			streetview_uri.append(destination.latitude + ",");
			streetview_uri.append(destination.longitude);
			streetview_uri.append("&key=AIzaSyBiDc85xw07AjJPGndg_rmvC8x5Lmczk2c");
			//Starts Google Maps app with given locations
			intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(streetview_uri.toString()));
			startActivity(intent);

			return true;
		case R.id.addtofavorites:
			//Add to favorites?
			return true;
		default:
			return super.onContextItemSelected(item);
		}
	}

	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub

	}
	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub

	}
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub

	}

	//Open context menu when map is long pressed
	@Override
	public void onMapLongClick(LatLng point) {
		onClick = point;
		contextMenuType = "map";
		openContextMenu(myView);

	}
	@Override
	public void onMapClick(LatLng point) {
		onClick = point;

	}

	public ArrayList<information> search(String keyword, double lat, double lng, int radius) {
		ArrayList<information> resultList = new ArrayList<information>();
		try {
			StringBuilder myBuilder = readUrl(keyword, lat, lng, radius);
			AsyncTask<StringBuilder, Void, StringBuilder> mySyncTask = new JSONParser().execute(myBuilder);
			StringBuilder newBuilder = null;
			JSONArray predsJsonArray = null;

			newBuilder = mySyncTask.get();
			predsJsonArray = convertToJson(newBuilder.toString());

			resultList = convertToInformation(predsJsonArray);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return resultList;
	}

	public Location getMyLocation() {
		return myLocation;
	}

	@SuppressLint("DefaultLocale")
	public void populateMap() {
		Intent myIntent = getIntent();
		Bundle myBundle = myIntent.getExtras();
		String searchInput = myBundle.get("search_input").toString();

		ArrayList<information> resultList = search(searchInput, myLocation.getLatitude(), myLocation.getLongitude(), 2000);
		
		for (int i = 0; i < resultList.size(); i++) {
			information aPiece = resultList.get(i);

			Log.i("aPiece address", aPiece.getAddress());

			// Customize markers with drawables
			determineType(aPiece);

			markerMap.put(aPiece.getAddress(), aPiece);
			keys.add(aPiece.getAddress());
		}

		// Display a message to let user know how many results were found
		Context context = getApplicationContext();
		CharSequence text = "Search Complete, I've found " + resultList.size() + " results for " + searchInput.toLowerCase() + " in the area!";
		int duration = Toast.LENGTH_SHORT;

		Toast toast = Toast.makeText(context, text, duration);
		toast.show();
	}

	public JSONArray convertToJson(String jsonResults) throws Exception{
		// Create a JSON object hierarchy from the results
		JSONObject jsonObj = new JSONObject(jsonResults.toString());
		JSONArray predsJsonArray = jsonObj.getJSONArray("results");
		return predsJsonArray;

	}

	public ArrayList<information> convertToInformation(JSONArray predsJsonArray) throws Exception{
		ArrayList<information> resultList = new ArrayList<information>();
		for (int i = 0; i < predsJsonArray.length(); i++) {
			information place = new information();

			// Get location coordinates from geometry string
			String geometry = predsJsonArray.getJSONObject(i).getString("geometry");
			LatLng resultLocation = regex(geometry);

			// Get icon image from the icon string
			String srcUrl = predsJsonArray.getJSONObject(i).getString("icon");
			AsyncTask<String, Void, Bitmap> mySyncTask = new ImageParser().execute(srcUrl);
			Bitmap icon = mySyncTask.get();

			// Get types from the types string
			String types = predsJsonArray.getJSONObject(i).getString("types");
			ArrayList<String> typeList = returnTypes(types);

			// Populate information instance with information
			place.setLocation(resultLocation);
			place.setTypeList(typeList);
			place.setName(predsJsonArray.getJSONObject(i).getString("name"));
			place.setAddress(predsJsonArray.getJSONObject(i).getString("vicinity"));
			place.setIcon(icon);
			// place.setPricing(Integer.parseInt(predsJsonArray.getJSONObject(i).getString("price_level")));
			// place.setRating(Double.parseDouble(predsJsonArray.getJSONObject(i).getString("rating")));

			Log.i("place address", place.getAddress());

			resultList.add(place);
		}
		return resultList;
	}

	public StringBuilder readUrl(String keyword, double lat, double lng, int radius) {

		StringBuilder sb = new StringBuilder(PLACES_API_BASE);
		sb.append(TYPE_SEARCH);
		sb.append(OUT_JSON);
		sb.append("?sensor=false");
		sb.append("&key=" + API_KEY);
		try {
			sb.append("&keyword=" + URLEncoder.encode(keyword, "utf8"));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		sb.append("&location=" + String.valueOf(lat) + "," + String.valueOf(lng));
		sb.append("&radius=" + String.valueOf(radius));

		return sb;
	}

	public ArrayList<String> returnTypes(String types) {
		ArrayList<String> returnList = new ArrayList<String>();
		ArrayList<String> rightList = new ArrayList<String>();
		StringTokenizer token = new StringTokenizer(types, "\",");

		while(token.hasMoreElements()) {
			returnList.add((String) token.nextElement());
		}

		rightList.add(returnList.get(1));
		rightList.add(returnList.get(2));
		rightList.add(returnList.get(3));

		return rightList;
	}

	public static LatLng regex(String aString) {
		LatLng coordinates;
		String regex = "";		
		String concatenatedCoordinates = "";

		regex = "[-+]?[0-9]*\\.?[0-9]+";

		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(aString);

		while (m.find()) {
			concatenatedCoordinates = concatenatedCoordinates.concat(m.group() + ",");
		}

		String[] latLngArray = concatenatedCoordinates.split(",");
		String lng = latLngArray[0];
		String lat = latLngArray[1];

		double latDouble = Double.parseDouble(lat);
		double lngDouble = Double.parseDouble(lng);
		coordinates = new LatLng(latDouble, lngDouble);

		return coordinates;
	}


	// Customizing different markers with different icons
	public void determineType(information arg) {
		ArrayList<String> typeList = arg.getTypeList();
		String type = typeList.get(0);

		if (type.compareTo("food") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.restaurant);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("cafe") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.coffee);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("bar") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bar);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("church") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cathedral);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("dentist") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dentist);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("restaurant") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.restaurant);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("doctor") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.acupuncture);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("park") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.parkandride);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("school") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.school);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("bakery") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.alien);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else if (type.compareTo("store") == 0) {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fooddeliveryservice);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		} else {
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pirates);

			mMap.addMarker(new MarkerOptions().
					position(arg.getLocation()).
					title(arg.getName()).
					icon(BitmapDescriptorFactory.fromBitmap(bitmap)).
					snippet(arg.getAddress())
					);
		}

		// Check to make sure the map is zoomed to a nice level
		mMap.animateCamera(CameraUpdateFactory.zoomTo(14));
	}
	/*
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		if ((keyCode == KeyEvent.KEYCODE_BACK))
		{
			finish();
		}
		return super.onKeyDown(keyCode, event);
	}
	 */
};